package at.ac.tuwien.ame;

import at.ac.tuwien.ame.EOperationPredicate;
import at.ac.tuwien.ame.FTLUtils;
import com.google.common.base.Objects;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Iterables;
import com.google.common.collect.Table;
import ftldsl.ModelElement;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtend2.lib.StringConcatenation;
import org.eclipse.xtext.xbase.lib.Conversions;
import org.eclipse.xtext.xbase.lib.Functions.Function0;
import org.eclipse.xtext.xbase.lib.IntegerRange;

/**
 * Maintains a table of scoped variables and their data types.
 */
@SuppressWarnings("all")
public class FTLCompiler {
  public static Resource resource;
  
  private static Logger logger = new Function0<Logger>() {
    public Logger apply() {
      String _name = FTLCompiler.class.getName();
      Logger _logger = Logger.getLogger(_name);
      return _logger;
    }
  }.apply();
  
  private static Table<String,EObject,String> varTable = new Function0<Table<String,EObject,String>>() {
    public Table<String,EObject,String> apply() {
      HashBasedTable<String,EObject,String> _create = HashBasedTable.<String, EObject, String>create();
      return _create;
    }
  }.apply();
  
  /**
   * Deletes all variables defined in the given scope.
   */
  public static void V_EVICT(final EObject scope) {
    StringConcatenation _builder = new StringConcatenation();
    _builder.append("removed variables of ");
    _builder.append(scope, "");
    FTLCompiler.logger.fine(_builder.toString());
    boolean _equals = Objects.equal(scope, null);
    if (_equals) {
      FTLCompiler.varTable.clear();
    } else {
      Map<String,String> _column = FTLCompiler.varTable.column(scope);
      _column.clear();
    }
  }
  
  /**
   * Sets or adds a new variable within the given scope.
   */
  public static String V_SET(final String name, final EObject scope, final String str) {
    String _xblockexpression = null;
    {
      StringConcatenation _builder = new StringConcatenation();
      _builder.append("added variable ");
      _builder.append(name, "");
      _builder.append(" as ");
      _builder.append(str, "");
      _builder.append(" in scope ");
      _builder.append(scope, "");
      FTLCompiler.logger.fine(_builder.toString());
      String _put = FTLCompiler.varTable.put(name, scope, str);
      _xblockexpression = (_put);
    }
    return _xblockexpression;
  }
  
  /**
   * Sets or adds a new variable within the given scope.
   */
  public static String V_SET(final String name, final EObject scope, final ModelElement me) {
    String _name = me.getName();
    String _V_SET = FTLCompiler.V_SET(name, scope, _name);
    return _V_SET;
  }
  
  /**
   * Retrieves the variable with the given name.
   */
  public static String V_GET(final String name) {
    String _xblockexpression = null;
    {
      final Map<EObject,String> vars = FTLCompiler.varTable.row(name);
      String _xifexpression = null;
      boolean _isEmpty = vars.isEmpty();
      if (_isEmpty) {
        _xifexpression = null;
      } else {
        Collection<String> _values = vars.values();
        Iterator<String> _iterator = _values.iterator();
        String _next = _iterator.next();
        _xifexpression = _next;
      }
      final String v = _xifexpression;
      StringConcatenation _builder = new StringConcatenation();
      _builder.append("get variable ");
      _builder.append(name, "");
      _builder.append(" as ");
      _builder.append(v, "");
      FTLCompiler.logger.fine(_builder.toString());
      _xblockexpression = (v);
    }
    return _xblockexpression;
  }
  
  /**
   * Attempts to determine the type of the given expression.
   */
  public static String inferType(final String e) {
    String _xblockexpression = null;
    {
      final String[] parts = e.split("\\.");
      String _get = ((List<String>)Conversions.doWrapArray(parts)).get(0);
      final String variable = FTLCompiler.V_GET(_get);
      boolean _equals = Objects.equal(variable, null);
      if (_equals) {
        return null;
      }
      EClassifier clazz = FTLUtils.classForName(FTLCompiler.resource, variable);
      String _xifexpression = null;
      boolean _equals_1 = Objects.equal(clazz, null);
      if (_equals_1) {
        _xifexpression = variable;
      } else {
        String _xifexpression_1 = null;
        int _size = ((List<String>)Conversions.doWrapArray(parts)).size();
        boolean _equals_2 = (_size == 1);
        if (_equals_2) {
          String _name = clazz.getName();
          _xifexpression_1 = _name;
        } else {
          String _xblockexpression_1 = null;
          {
            boolean _contains = variable.contains("Factory.eINSTANCE.create");
            if (_contains) {
              String type = FTLUtils.getUnqualifiedName(variable);
              int _length = type.length();
              int _minus = (_length - 2);
              String _substring = type.substring(6, _minus);
              type = _substring;
              EClass _classForName = FTLUtils.classForName(FTLCompiler.resource, type);
              clazz = ((EClassifier) _classForName);
              String _xifexpression_2 = null;
              Class<? extends Object> _instanceClass = clazz.getInstanceClass();
              boolean _notEquals = (!Objects.equal(_instanceClass, null));
              if (_notEquals) {
                Class<? extends Object> _instanceClass_1 = clazz.getInstanceClass();
                String _name_1 = _instanceClass_1.getName();
                _xifexpression_2 = _name_1;
              } else {
                String _name_2 = clazz.getName();
                _xifexpression_2 = _name_2;
              }
              return _xifexpression_2;
            }
            StringConcatenation _builder = new StringConcatenation();
            _builder.append("Variable or method ");
            String _get_1 = ((List<String>)Conversions.doWrapArray(parts)).get(0);
            _builder.append(_get_1, "");
            _builder.append(" undefined or not visible in scope: ");
            _builder.append(e, "");
            final String msg = _builder.toString();
            int _size_1 = ((List<String>)Conversions.doWrapArray(parts)).size();
            int _minus_1 = (_size_1 - 1);
            IntegerRange _upTo = new IntegerRange(1, _minus_1);
            for (final int i : _upTo) {
              {
                String _get_2 = ((List<String>)Conversions.doWrapArray(parts)).get(i);
                final EStructuralFeature field = ((EClass) clazz).getEStructuralFeature(_get_2);
                boolean _notEquals_1 = (!Objects.equal(field, null));
                if (_notEquals_1) {
                  boolean _isMany = field.isMany();
                  if (_isMany) {
                    StringConcatenation _builder_1 = new StringConcatenation();
                    _builder_1.append("org.eclipse.emf.common.util.EList<");
                    EClassifier _eType = field.getEType();
                    String _name_3 = _eType.getName();
                    _builder_1.append(_name_3, "");
                    _builder_1.append(">");
                    return _builder_1.toString();
                  }
                  EClassifier _eType_1 = field.getEType();
                  clazz = _eType_1;
                } else {
                  EList<EOperation> _eAllOperations = ((EClass) clazz).getEAllOperations();
                  String _get_3 = ((List<String>)Conversions.doWrapArray(parts)).get(i);
                  EOperationPredicate _eOperationPredicate = new EOperationPredicate(_get_3);
                  final EOperation method = Iterables.<EOperation>find(_eAllOperations, _eOperationPredicate, null);
                  boolean _equals_3 = Objects.equal(method, null);
                  if (_equals_3) {
                    IllegalArgumentException _illegalArgumentException = new IllegalArgumentException(msg);
                    throw _illegalArgumentException;
                  }
                  EClassifier _eType_2 = method.getEType();
                  clazz = _eType_2;
                }
              }
            }
            String _xifexpression_3 = null;
            Class<? extends Object> _instanceClass_2 = clazz.getInstanceClass();
            boolean _notEquals_1 = (!Objects.equal(_instanceClass_2, null));
            if (_notEquals_1) {
              Class<? extends Object> _instanceClass_3 = clazz.getInstanceClass();
              String _name_3 = _instanceClass_3.getName();
              _xifexpression_3 = _name_3;
            } else {
              String _name_4 = clazz.getName();
              _xifexpression_3 = _name_4;
            }
            _xblockexpression_1 = (_xifexpression_3);
          }
          _xifexpression_1 = _xblockexpression_1;
        }
        _xifexpression = _xifexpression_1;
      }
      _xblockexpression = (_xifexpression);
    }
    return _xblockexpression;
  }
}
